﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using DoeObjects;
using System.Collections.Generic;

namespace DoeLibsTest
{
    [TestClass]
    public class Title_Test
    {
        [TestInitialize]
        public void init()
        {
            TestContext.initialize();
        }

        [TestMethod]
        public void Test_getById()
        {
            Title t1 = Helper.createTitle(true);

            Title loaded = Title.getById(t1.TitleId);

            Assert.AreEqual(t1.BookTitle, loaded.BookTitle);
            Assert.AreEqual(t1.EditionNumber, loaded.EditionNumber);
            Assert.AreEqual(t1.EditionYear, loaded.EditionYear);
            Assert.AreEqual(t1.FirstEditionYear, loaded.FirstEditionYear);
            Assert.AreEqual(t1.ISBN10, loaded.ISBN10);
            Assert.AreEqual(t1.ISBN13, loaded.ISBN13);
            Assert.AreEqual(t1.Publisher.PublisherId, loaded.Publisher.PublisherId);
            Assert.AreEqual(t1.Publisher.Name, loaded.Publisher.Name);
            Assert.AreEqual(t1.TitleId, loaded.TitleId);
        }

        [TestMethod]
        public void Test_isAvailable()
        {
            User borrower = Helper.createUserObject(false, true);
            User borrower2 = Helper.createUserObject(false, true);
            User borrower3 = Helper.createUserObject(false, true);
            Title t1 = Helper.createTitle(true);

            //test if is available without loanable
            Assert.IsTrue(!Title.isAvalible(t1.TitleId), "A title without loanable can't be available");

            //test if is available with checkedIn loanable
            Loanable lb = Helper.createLoanable(true);
            lb.Title = t1;
            Assert.IsTrue(lb.save(), "could not save loanable");

            Assert.IsTrue(Title.isAvalible(t1.TitleId), "title with one checkedIn loanable should be available");

            //check if is available with checkedOut loanable
            Loan loan = lb.checkOut(borrower);
            Assert.IsTrue(loan != null, "could not checkout loanable");

            Assert.IsTrue(!Title.isAvalible(t1.TitleId), "Title has only one loanable and this one is checked out. Title mustn't be available");

            //create another loanable which is checkedIn
            Loanable lb2 = Helper.createLoanable(true);
            Assert.IsTrue(lb2 != null, "could not create 2nd loanable");
            lb2.Title = t1;
            Assert.IsTrue(lb2.save(), "could not assign title to 2nd loanable");

            Assert.IsTrue(Title.isAvalible(t1.TitleId), "one of two loanables is available. Title should be available");

            //test with 2 available loanables:

            //checkIn lb
            Assert.IsTrue(loan.checkIn(), "could not checkIn loan1");
            Assert.IsTrue(Title.isAvalible(t1.TitleId), "two of two loanables are available. Title should be available");

            //checkOut both loanables
            Loan loan2 = lb.checkOut(borrower);
            Loan loan3 = lb2.checkOut(borrower2);
            Loan loan4 = lb2.checkOut(borrower3);
            Assert.IsNull(loan4, "a loanable cannot be checked out multiple times if it is not checkedIn");
            Assert.IsTrue(loan2 != null, "could not checkout loanable1");
            Assert.IsTrue(loan3 != null, "could not checkout loanable2");

            Assert.IsTrue(!Title.isAvalible(t1.TitleId), "two of two loanables are checkedOut. Title mustn't be available");

            Assert.IsTrue(loan2.checkIn(), "could not checkIn loanable again");
            Assert.IsTrue(Title.isAvalible(t1.TitleId), "one of two loanables is available. Title should be available");
            Assert.IsTrue(loan3.checkIn(), "could not checkIn loanable again");
            Assert.IsTrue(Title.isAvalible(t1.TitleId), "two of two loanables is available. Title should be available");
        }

        [TestMethod]
        public void Test_isAvailableWithReservations()
        {
            User borrower = Helper.createUserObject(false, true);
            User borrower2 = Helper.createUserObject(false, true);
            User borrower3 = Helper.createUserObject(false, true);
            User borrower4 = Helper.createUserObject(false, true);
            Title t1 = Helper.createTitle(true);
            Title t2 = Helper.createTitle(true);
            Loanable lbl1 = Helper.createLoanable(true, t1);
            Loanable lbl2 = Helper.createLoanable(true, t1);

            //make first reservation
            Assert.IsTrue(Reservation.createReservation(t1.TitleId, borrower.UserId), "could not save reservation1");
            Assert.IsTrue(Title.isAvalible(t1.TitleId), "there is only one reservation and two loanables. title should be available");

            //make 2nd reservation
            Assert.IsTrue(Reservation.createReservation(t1.TitleId, borrower2.UserId), "could not save reservation2");
            Assert.IsFalse(Title.isAvalible(t1.TitleId), "there are two reservation and two loanables. title mustn't be available");

            //checkOut loanable1:
            Loan loan1 = lbl1.checkOut(borrower);
            Assert.IsTrue(loan1 != null, "could not check out lbl1");

            Assert.IsFalse(Title.isAvalible(t1.TitleId), "there is one reservation and two loanables - one loanable is already checked out. title mustn't be available");

            //try to checkout lbl2 without having a reservation
            Loan loan2 = lbl2.checkOut(borrower3);
            Assert.IsNull(loan2, "lbl2 mustn't be checkOut-able if you don't have a reservation");

            //create a reservation for borrower3
            Assert.IsTrue(Reservation.createReservation(t1.TitleId, borrower3.UserId), "could not create reservation");
            
            Assert.IsFalse(Title.isAvalible(t1.TitleId), "there are 3 reservation and two loanables. title mustn't be available");
    
            //try to checkOut lbl2 as borrower3 (who placed the reservation later) <- might be not part of testing isAvailable, but lets test it...
            Loan loan3 = lbl1.checkOut(borrower3);
            Assert.IsNull(loan3, "lbl1 is still borrowed. can't be checked out");

            Loan loan4 = lbl2.checkOut(borrower3);
            Assert.IsNull(loan4, "borrower3 is last in queue. borrower2 reserved the title before. borrower3 mustn't be allowed to check out the title before borrower2");

            //create a new loanable
            Loanable lbl3 = Helper.createLoanable(true, t1);

            Assert.IsFalse(Title.isAvalible(t1.TitleId), "we have 3 loanables. one is checked out and 2 reservations. title mustn't be available");

            //check out lbl2 as borrower3 (should work now, because we have enough loanables)
            Loan loan5 = lbl2.checkOut(borrower3);
            Assert.IsTrue(loan5 != null, "could not checkOut lbl2");

            Assert.IsFalse(Title.isAvalible(t1.TitleId), "we have 3 loanables. two are checked out and 1 reservations. title mustn't be available");

            //checkIn lbl2 again
            Assert.IsTrue(loan5.checkIn(), "could not checkIn lbl2");
            Assert.IsTrue(Title.isAvalible(t1.TitleId), "3 loanables, 1 reservation, 1 checked out, 2 are available. title must be available");

            //borrower4 wants to check out a loanable
            Loan loan6 = lbl3.checkOut(borrower4);
            Assert.IsTrue(loan6 != null, "2 loanables are checkedIn, one is checkedOut, only one reservation. A user without a reservation should be able the check a loanable out");

            Assert.IsFalse(Title.isAvalible(t1.TitleId), "only 1 loanable is checkedIn. this is reserved. the title mustn't be available");

            //check out lbl2 as borrower2
            Loan loan7 = lbl2.checkOut(borrower2);
            Assert.IsTrue(loan7 != null, "could not checkOut lbl2");

            Assert.IsFalse(Title.isAvalible(t1.TitleId), "all loanables are checkedOut. title mustn't be available");

            //check in lbl2
            Assert.IsTrue(loan7.checkIn(), "could not checkIn lbl2 again");
            Assert.IsTrue(Title.isAvalible(t1.TitleId), "one loanable is checkedIn again. no reservations left. title must be available");

            //check in lbl3
            Assert.IsTrue(loan6.checkIn(), "could not checkIn lbl3 again");
            Assert.IsTrue(Title.isAvalible(t1.TitleId), "two loanables are checkedIn again. no reservations left. title must be available");

            //checkIn lbl1
            Assert.IsTrue(loan1.checkIn(), "could not checkIn lbl1 again");
            Assert.IsTrue(Title.isAvalible(t1.TitleId), "two loanables are checkedIn again. no reservations left. title must be available");
        }

        [TestMethod]
        public void test_isReserved()
        {
            User borrower = Helper.createUserObject(false, true);
            User borrower2 = Helper.createUserObject(false, true);
            User borrower3 = Helper.createUserObject(false, true);
            User borrower4 = Helper.createUserObject(false, true);
            Title t1 = Helper.createTitle(true);
            Title t2 = Helper.createTitle(true);
            Loanable lbl1 = Helper.createLoanable(true, t1);

            //title without loanable:
            Assert.IsFalse(Title.isReserved(t2.TitleId, false));
            Assert.IsFalse(Title.isReserved(t2.TitleId, true));

            //add a loanable:
            Assert.IsFalse(Title.isReserved(t1.TitleId, false), "one loanable, but no reservation");
            Assert.IsFalse(Title.isReserved(t1.TitleId, true), "one loanable, but no reservation");

            //add a reservation
            Assert.IsTrue(Reservation.createReservation(t1.TitleId, borrower.UserId), "could not create reservation");
            Assert.IsFalse(Title.isReserved(t1.TitleId, false), "a new reservation was created before and one loanable was available. The createReservation should have set the LoanRecalled property to true");
            Assert.IsTrue(Title.isReserved(t1.TitleId, true), "a new reservation was created before and one loanable was available. The createReservation should have set the LoanRecalled property to true");

            //add another reservation
            Assert.IsTrue(Reservation.createReservation(t1.TitleId, borrower2.UserId), "could not create 2nd reservation");
            Assert.IsTrue(Title.isReserved(t1.TitleId, false), "another reservation was created. only one loanable was available. The first reservation should have set the LoanRecalled property to true");
            Assert.IsTrue(Title.isReserved(t1.TitleId, true), "another reservation was created. only one loanable was available. The 2nd reservation should have set the LoanRecalled property to false");

            //create a new reservation
            Assert.IsTrue(Reservation.createReservation(t1.TitleId, borrower3.UserId), "could not create 3rd reservation");

            Assert.IsTrue(Title.isReserved(t1.TitleId, false), "another reservation was created. only one loanable was available. The first reservation should have set the LoanRecalled property to true");
            Assert.IsTrue(Title.isReserved(t1.TitleId, true), "another reservation was created. only one loanable was available. The 2nd reservation should have set the LoanRecalled property to false");

            //add another loanable
            Loanable lbl2 = Helper.createLoanable(true, t1);

            ////add a 3rd reservation
            ////HACK-START (this section should be covered when a new loanable is created, that it checks for reservations and sets the LoanRecalled property)
            //res3.AvailableDate = DateTime.Now;
            //res3.LoanRecalled = true;
            //Assert.IsTrue(res3.save(), "could not update reservation 3");
            ////HACK-END

            Assert.IsTrue(Title.isReserved(t1.TitleId, false), "another reservation was created. only one loanable was available. The first reservation should have set the LoanRecalled property to true");
            Assert.IsTrue(Title.isReserved(t1.TitleId, true), "another reservation was created. only one loanable was available. The 2nd reservation should have set the LoanRecalled property to false");

            //checkOut and checkIn lbl
            Loan loan1 = lbl1.checkOut(borrower);
            Assert.IsTrue(loan1 != null, "could not checkOut loanable1");

            Assert.IsTrue(Title.isReserved(t1.TitleId, false));
            Assert.IsTrue(Title.isReserved(t1.TitleId, true));

            Assert.IsTrue(loan1.checkIn());

            //now the 2nd reservation should be have loanRecalled too
            Assert.IsFalse(Title.isReserved(t1.TitleId, false), "there should be no reservation where the loanable is not recalled");
            Assert.IsTrue(Title.isReserved(t1.TitleId, true));

            //checkOut and checkIn lbl again
            Loan loan2 = lbl1.checkOut(borrower2);
            Assert.IsTrue(loan2 != null, "could not checkOut loanable1");

            Assert.IsFalse(Title.isReserved(t1.TitleId, false));
            Assert.IsTrue(Title.isReserved(t1.TitleId, true), "There should be one reservation where loanRecalled = true, because we have 2 loanables");

            Assert.IsTrue(loan2.checkIn());

            //now the 2nd reservation should be have loanRecalled too
            Assert.IsFalse(Title.isReserved(t1.TitleId, false), "there should be no reservation where the loanable is not recalled");
            Assert.IsTrue(Title.isReserved(t1.TitleId, true), "only one reservation is left. there the loanrecalled field should be set to true");

            //checkOut and in
            Loan loan3 = lbl2.checkOut(borrower3);
            Assert.IsTrue(loan3 != null, "could not checkOut loanable3");
            Assert.IsTrue(loan3.checkIn(), "could not checkIn loan3 again");

            Assert.IsFalse(Title.isReserved(t1.TitleId, false), "there should be no reservation where the loanable is not recalled");
            Assert.IsFalse(Title.isReserved(t1.TitleId, true), "all reservations are handled");

        }
    }
}
